import React from 'react';
import PropTypes from 'prop-types';
import { noop, genAppUid, genFileUid, isPlainObject } from './utils';

class IframeUpload extends React.Component {
    static propTypes = {
        /**
         * [role=application]自定义样式
         */
        style: PropTypes.object,
        /**
         * [role=application]自定义className
         */
        className: PropTypes.string,

        /**
         * input[type=file]的属性id
         */
        id: PropTypes.string,
        /**
         * input[type=file]的属性name
         * 上传文件的字段名，需与后端接口对应
         */
        name: PropTypes.string.isRequired,
        /**
         * input[type=file]的属性disabled
         * 上传组件是否可用
         */
        disabled: PropTypes.bool,
        /**
         * input[type=file]的属性accept
         * 接受上传的文件类型 (image/png, image/jpg, .doc, .ppt) 详见 [input accept attribute](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input#attr-accept)
         */
        accept: PropTypes.string,

        /**
         * 上传地址
         * 即form表单的action属性
         */
        action: PropTypes.string.isRequired,
        /**
         * 上传文件时携带附加参数
         */
        data: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
        headers: PropTypes.object,

        /**
         * 是否自动上传？
         */
        autoUpload: PropTypes.bool,
        /**
         * 事件回调函数，当选了文件时触发
         */
        onSelect: PropTypes.func,
        /**
         * 上传前执行的回调函数
         */
        beforeUpload: PropTypes.func,
        /**
         * 开始上传
         */
        onStart: PropTypes.func,
        /**
         * 上传成功后的回调函数
         */
        onSuccess: PropTypes.func,
        /**
         * 上传失败时的回调函数
         */
        onError: PropTypes.func,
        /**
         * 取消上传时的回调函数
         */
        onAbort: PropTypes.func,


        children: PropTypes.node,
    };

    static defaultProps = {
        name: 'file',
        autoUpload: true,

        onSelect: noop,
        beforeUpload: noop,
        onStart: noop,
        onSuccess: noop,
        onError: noop,
        onAbort: noop,
    };

    file = {};
    uid = genAppUid();
    uploading = false;

    constructor(props) {
        super(props);
        this.domain = '';
        if (typeof document !== 'undefined' && document.domain) {
            this.domain = document.domain;
        }
    }

    onIframeLoad = () => {
        if (!this.uploading) {
            return;
        }
        const { props, file } = this;
        let response;
        try {
            const doc = this.iframeRef.contentDocument;
            const script = doc.getElementsByTagName('script')[0];
            if (script && script.parentNode === doc.body) {
                doc.body.removeChild(script);
            }
            response = doc.body.innerHTML;
            props.onSuccess(response, file);
        } catch (err) {
            console.warn(
                'cross domain error for Upload. Maybe server should return document.domain script.'
            );
            response = 'cross-domain';
            props.onError(err, null, file);
        }

        this.endUpload();
    };

    onSelect = e => {
        this.file = {
            uid: genFileUid(),
            name: e.target.value,
        };
        
        this.props.onSelect([this.file]);

        if (this.props.autoUpload) {
            this.startUpload();
        }
    };

    startUpload() {
        this.upload(this.file);
    }

    upload(file) {
        if (!this.uploading) {
            this.uploading = true;
        }

        const {
            beforeUpload,
            action,
            name,
            data
        } = this.props;

        if (!beforeUpload) {
            return this.post(file);
        }

        const requestData = {
            action,
            name,
            data,
        };

        const before = beforeUpload(file, requestData);

        if (before && before.then) {
            before.then(data => {
                this.post(file, data);
            }, () => {
                this.endUpload();
            });
        } else if (before !== false) {
            this.post(file, isPlainObject(before) ? before : undefined);
        } else {
            this.endUpload();
        }
    }

    endUpload() {
        this.file = {};
        if (this.uploading) {
            this.uploading = false;
        }
    }

    abort(file) {
        if (file) {
            let uid = file;
            if (file && file.uid) {
                uid = file.uid;
            }
            if (uid === this.file.uid) {
                this.endUpload();
            }
        } else {
            this.endUpload();
        }
    }

    post(file, requestOption = {}) {
        const formNode = this.formRef;
        const dataSpan = this.dataRef;
        const fileInput = this.inputRef;

        let { data: propsData } = this.props;
        if (typeof propsData === 'function') {
            propsData = propsData(file);
        }

        const {
            action,
            name,
            data
        } = requestOption;
        if (name) {
            fileInput.setAttribute('name', name);
        }

        if (action) {
            formNode.setAttribute('action', action);
        }

        if (data) {
            propsData = data;
        }

        const inputs = document.createDocumentFragment();
        for (const key in propsData) {
            if (data.hasOwnProperty(key)) {
                const input = document.createElement('input');
                input.setAttribute('name', key);
                input.value = propsData[key];
                inputs.appendChild(input);
            }
        }
        dataSpan.appendChild(inputs);
        formNode.submit();
        dataSpan.innerHTML = '';
        this.props.onStart(file);
    }

    saveIFrameRef = ref => {
        this.iframeRef = ref;
    };
    saveFormRef = ref => {
        this.formRef = ref;
    };
    saveDataRef = ref => {
        this.dataRef = ref;
    };
    saveInputRef = ref => {
        this.inputRef = ref;
    };

    render() {
        const {
            disabled,
            className,
            children,
            accept,
            name,
            style,

            action
        } = this.props;
        
        const iframeName = `${name}-${this.uid}-iframe`;

        let iframe = null;
        if (!disabled) {
            iframe = (
                <iframe
                    title={iframeName}
                    ref={this.saveIFrameRef}
                    name={iframeName}
                    onLoad={this.onIframeLoad}
                    style={{ display: 'none' }}
                />
            );
        }

        return (
            <div
                role="application"
                style={{
                    position: 'relative',
                    zIndex: 0,
                    display: 'inline-block',
                    cursor: 'pointer',
                    ...style,
                }}
                className={className}
            >
                {iframe}
                <form
                    ref={this.saveFormRef}
                    method="POST"
                    action={action}
                    encType="multipart/form-data"
                    target={iframeName}
                    style={{
                        position: 'absolute',
                        top: 0,
                        right: 0,
                        bottom: 0,
                        left: 0,
                        zIndex: 1,
                        overflow: 'hidden',
                        opacity: 0
                    }}
                >
                    <input
                        name="_documentDomain"
                        value={this.domain}
                        type="hidden"
                    />
                    <div ref={this.saveDataRef} />
                    <input
                        key={this.state.uid}
                        type="file"
                        id={id}
                        name={name}
                        accept={accept}
                        multiple={multiple}
                        disabled={disabled}
                        onChange={this.onSelect}
                        style={{
                            position: 'absolute',
                            top: 0,
                            right: 0,
                            fontSize: 9999,
                            outline: 'none',
                            cursor: 'pointer',
                        }}
                        ref={this.saveInputRef}
                    />
                </form>
                {children}
            </div>
        );
    }
}

export default IframeUpload;
